import { GameEventHandler } from './Interface';
import { BaseEventEmitter, Observer } from './Observer';

/**
 * GameEvent 类用于扩展神岛引擎自带游戏中的事件，继承自 BaseEventEmitter 类。
 *
 * @template TEventChannel 事件通道的类型，用于指定事件的种类。
 */
class GameEvent<TEventChannel> extends BaseEventEmitter<TEventChannel> {
  /**
   * 构造函数，初始化 GameEvent 实例。
   *
   * @param eventSources 事件源对象，通常包含一系列事件处理函数。
   */
  constructor(private eventSources: any) {
    super();
    try {
      // 初始化事件源，将事件源中的事件处理函数绑定到当前事件管理器。
      this.init(eventSources);
    } catch {
      console.error('初始化事件源失败，该端下不存在事件源:', eventSources);
    }
  }

  /**
   * 添加事件监听器，当指定事件发生时，回调函数将被调用。
   *
   * @template T 事件类型，从 TEventChannel 中推导得出。
   * @param eventName 事件名称。
   * @param callback 事件发生时调用的回调函数。
   * @param target 可选参数，回调函数绑定的目标对象。
   * @returns 返回 GameEventClass 实例，支持链式调用。
   */
  on<T extends TEventChannel>(
    eventName: T,
    callback: GameEventHandler<T>,
    target?: any
  ): this {
    return this.addListener(eventName, new Observer(callback, target || this));
  }

  /**
   * 添加一次性事件监听器，当指定事件发生时，回调函数仅被调用一次，然后监听器自动移除。
   *
   * @template T 事件类型，从 TEventChannel 中推导得出。
   * @param eventName 事件名称。
   * @param callback 事件发生时调用的回调函数。
   * @param target 可选参数，回调函数绑定的目标对象。
   * @returns 返回 GameEventClass 实例，支持链式调用。
   */
  once<T extends TEventChannel>(
    eventName: T,
    callback: GameEventHandler<T>,
    target?: any
  ): this {
    return this.addListener(
      eventName,
      new Observer(callback, target || this, true)
    );
  }

  /**
   * 移除事件监听器。
   * - 如果未指定回调函数和目标对象，将移除所有该事件的监听器。
   * - 如果仅指定了目标对象，将移除该目标对象绑定的所有监听器。
   * - 如果仅指定了回调函数，将移除该回调函数绑定的所有监听器。
   *
   * @template T 事件类型，从 TEventChannel 中推导得出。
   * @param eventName 事件名称。
   * @param callback 可选参数，要移除的回调函数。
   * @param target 可选参数，要移除的回调函数绑定的目标对象。
   * @returns 返回 GameEventClass 实例，支持链式调用。
   */
  off<T extends TEventChannel>(
    eventName: T,
    callback?: GameEventHandler<T>,
    target?: any
  ): this {
    const observers = this.listeners.get(eventName);
    if (!observers) {
      return this;
    }

    if (!callback && !target) {
      this.removeAllListeners(eventName);
      return this;
    }

    observers.forEach((observer) => {
      if (
        (target && !callback && observer.compar(target)) ||
        (callback && !target && observer.callback === callback) ||
        (callback &&
          target &&
          observer.callback === callback &&
          observer.compar(target))
      ) {
        this.removeListener(eventName, observer);
      }
    });

    return this;
  }

  /**
   * 触发指定事件，所有绑定到该事件的监听器将被通知。
   *
   * @template T 事件类型，从 TEventChannel 中推导得出。
   * @param eventName 事件名称。
   * @param event 事件对象，传递给监听器。
   * @returns 返回 GameEventClass 实例，支持链式调用。
   */
  private emit<T extends TEventChannel>(
    eventName: T,
    event: T extends GameEventChannel<infer E> ? E : never
  ): this {
    this.notifyListeners(eventName, event);
    return this;
  }

  /**
   * 移除指定目标对象绑定的所有事件监听器。
   *
   * @param target 目标对象，其绑定的监听器将被移除。
   * @returns 返回 GameEventClass 实例，支持链式调用。
   */
  targetOff(target: any): this {
    if (!target) {
      return this;
    }

    this.listeners.forEach((observers, eventName) => {
      observers.forEach((observer) => {
        if (observer.compar(target)) {
          this.removeListener(eventName, observer);
        }
      });
    });

    return this;
  }

  /**
   * 移除所有事件监听器。
   *
   * @returns 返回 GameEventClass 实例，支持链式调用。
   */
  removeAll(): this {
    this.removeAllListeners();
    return this;
  }

  /**
   * 初始化事件源，将事件源中的事件处理函数绑定到当前事件管理器。
   *
   * @param type 事件源对象，通常包含一系列事件处理函数。
   */
  private init(type: any) {
    Object.getOwnPropertyNames(type).forEach((key) => {
      const property = type[key];
      if (key.startsWith('on') && typeof property === 'function') {
        property.call(type, (event: any) => {
          this.emit(property, event);
        });
      }
    });
  }
}

/**
 * 服务端世界游戏事件的实例化。
 *
 * GameWorldEvent 是一个 {@link GameWorld} 世界管理相关事件实例，用于处理与游戏世界相关的事件。
 * 它允许通过 `on`、`once`、`off` 等方法注册或移除事件监听器，并支持事件的触发和通知。
 *
 * 示例用法：
 * ```typescript
 * // 注册事件监听器，当玩家加入游戏时触发
 * GameWorldEvent.on(world.onPlayerJoin, ({ entity }) => {
 *   console.log(entity.player.name);
 * });
 * ```
 */
export const GameWorldEvent = new GameEvent<GameEventChannel<any>>(
  typeof world !== 'undefined' ? world : undefined
);
